package com.yunxin.core.application.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.StrBuilder;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.yunxin.common.exception.BusinessException;
import com.yunxin.common.result.PageResult;
import com.yunxin.common.result.ResultCode;
import com.yunxin.core.application.assembler.ProductAssembler;
import com.yunxin.core.application.command.product.AdvanceChargePageCommand;
import com.yunxin.core.application.command.product.ProductAdvanceChargeRecordPageCommand;
import com.yunxin.core.application.command.product.ProductPageCommand;
import com.yunxin.core.application.dto.product.*;
import com.yunxin.core.application.service.ProductQueryAdminService;
import com.yunxin.core.domain.external.merchant.MerchantExternalService;
import com.yunxin.core.domain.external.merchant.MerchantList;
import com.yunxin.core.domain.external.merchant.MerchantPageDTO;
import com.yunxin.core.domain.external.product.ProductBanDetails;
import com.yunxin.core.infra.common.product.enums.ProductPlacementRegionTypeEnum;
import com.yunxin.core.infra.persistence.entity.product.*;
import com.yunxin.core.infra.persistence.entity.product.query.condition.ProductPageCondition;
import com.yunxin.core.infra.persistence.entity.product.query.result.*;
import com.yunxin.core.infra.persistence.mapper.merchant.MerchantInfoExtendMapper;
import com.yunxin.core.infra.persistence.mapper.product.*;
import com.yunxin.core.utils.infra.utils.DateFormatUtils;
import com.yunxin.core.utils.infra.utils.PageQueryUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author zero°
 * @since 2023-07-13
 */
@Service
@RequiredArgsConstructor
public class ProductQueryAdminServiceImpl implements ProductQueryAdminService {

    private final ProductInfoExtendMapper productInfoExtendMapper;

    private final ProductCustomConfigExtendMapper productCustomConfigExtendMapper;

    private final ProductThrowInConfigExtendMapper productThrowInConfigExtendMapper;

    private final ProductUnitPriceConfigExtendMapper productUnitPriceConfigExtendMapper;

    private final ProductUnitPriceConfigVersionExtendMapper productUnitPriceConfigVersionExtendMapper;

    private final ProductAdvanceChargeRecordExtendMapper productAdvanceChargeRecordExtendMapper;

    private final MerchantExternalService merchantExternalService;

    private final ProductAssembler productAssembler;



    @Override
    public ProductPromotionInfoDTO getProductPromotionInfo(Long productId) {
        ProductPromotionInfoResult result = productInfoExtendMapper.selectByProductId(productId);
        if (Objects.isNull(result)) {
            throw new BusinessException(ResultCode.DATA_NO_EXIST_ERROR);
        }
        return productAssembler.toProductPromotionInfoDTO(result);
    }

    @Override
    public ProductCustomConfigDTO getProductPromotionCustomConfig(Long productId) {
        ProductCustomConfigDO customConfig = productCustomConfigExtendMapper.selectByProductId(productId);
        if (Objects.isNull(customConfig)) {
            throw new BusinessException(ResultCode.DATA_NO_EXIST_ERROR);
        }
        return productAssembler.toProductCustomConfigDTO(customConfig);
    }

    @Override
    public ProductThrowInConfigDTO getProductPromotionThrowInConfig(Long productId) {
        ProductThrowInConfigDOWithBLOBs throwInConfig = productThrowInConfigExtendMapper.selectByProductId(productId);
        if (Objects.isNull(throwInConfig)) {
            throw new BusinessException(ResultCode.DATA_NO_EXIST_ERROR);
        }
        ProductThrowInConfigDTO inConfigDTO = productAssembler.toProductThrowInConfigDTO(throwInConfig);
        inConfigDTO.setBeProductId(throwInConfig.getBeProductId());
        inConfigDTO.setUserProtocol(throwInConfig.getUserProtocol());
        inConfigDTO.setIsLogin(throwInConfig.getIsLogin());
        return inConfigDTO;
    }

    @Override
    public ProductUnitPriceConfigDTO geProductPromotionUnitPriceConfig(Long productId) {
        ProductUnitPriceConfigDO unitPriceConfig = productUnitPriceConfigExtendMapper.selectByProductId(productId);
        if (Objects.isNull(unitPriceConfig)) {
            throw new BusinessException(ResultCode.DATA_NO_EXIST_ERROR);
        }
        return productAssembler.toProductUnitPriceConfigDTO(unitPriceConfig);
    }

    @Override
    public PageResult<ProductPageDTO> productPageQuery(ProductPageCommand command) {
        ProductPageCondition condition = productAssembler.fromProductPageCommand(command);
        if (Objects.nonNull(command.getAbutmentType())) condition.setAbutmentType(command.getAbutmentType());
        long total = productInfoExtendMapper.countProductPageQuery(condition);
        if (total == 0) {
            return PageResult.success(Collections.emptyList(), total);
        }
        condition.setOffset(PageQueryUtils.X.getOffset(command.getPageNum(), command.getPageSize()));
        List<ProductPageResult> results = productInfoExtendMapper.productPageQuery(condition);
        if (CollUtil.isEmpty(results)) {
            return PageResult.success(Collections.emptyList(), total);
        }
        Set<Long> merchantIds = results.stream().map(ProductPageResult::getMerchantId).collect(Collectors.toSet());
        Map<Long, MerchantList> merchantListResultMap = merchantExternalService.listMerchantByIds(merchantIds).stream().collect(Collectors.toMap(MerchantList::getId, Function.identity()));
        List<ProductPageDTO> dtoList = results.stream().map(x -> {
            ProductPageDTO dto = productAssembler.toProductPageDTO(x);
            dto.setAbutmentType(x.getAbutmentType());
            String placementPeriod = x.getPlacementPeriod();
            if (StrUtil.isNotBlank(placementPeriod)) {
                //JSONObject obj = JSONUtil.parseArray(placementPeriod).getJSONObject(0);
                JSONArray objects = JSONUtil.parseArray(placementPeriod);
                StrBuilder append = StrBuilder.create(x.getLimitDay()).append(" ");
                boolean flag=false;
                for (Object object : objects) {
                    JSONObject obj = (JSONObject)object;
                    String endTime = obj.getStr("endTime");
                    String startTime = obj.getStr("startTime");
                    Integer limitCount = obj.getInt("limitCount");
                    append = append.append(startTime).append("-").append(endTime);
                    if (!ObjectUtils.isEmpty(limitCount)) {
                        append.append("(").append(limitCount).append(")");
                    }
                    append.append(";");
                    flag=true;
                }
                if (flag) {
                    dto.setLimitDay(append.toString());
                } else {
                    dto.setLimitDay(x.getLimitDay()+" 全天;");
                }
            }
            MerchantList result = merchantListResultMap.get(x.getMerchantId());
            Optional.ofNullable(result).ifPresent(m -> {
                dto.setMerchantName(m.getMerchantName());
                dto.setMerchantType(m.getMerchantType());
                if (ProductPlacementRegionTypeEnum.NATIONWIDE.getValue().equals(x.getPlacementRegionType())) {
                    dto.setProductRange(ProductPlacementRegionTypeEnum.NATIONWIDE.getLabel());
                } else {
                    dto.setProductRange(x.getPlacementRegion());
                }
            });
            return dto;
        }).toList();
        return PageResult.success(dtoList, total);
    }

    @Override
    public List<ProductDetailDTO> listProductByMerchantId(Long merchantId, Integer productType) {
        List<ProductDetailResult> results = productInfoExtendMapper.listProductDetailByMerchantId(merchantId, productType);
        return results.stream().map(productAssembler::toProductDetailDTO).toList();
    }

    @Override
    public List<ProductListDTO> listProduct() {
        List<ProductInfoDO> list = productInfoExtendMapper.listProduct();
        if (CollUtil.isEmpty(list)) {
            return CollUtil.newArrayList();
        }
        Set<Long> merchantIds = list.stream().map(ProductInfoDO::getMerchantId).collect(Collectors.toSet());
        Map<Long, MerchantList> merchantListResultMap = merchantExternalService.listMerchantByIds(merchantIds).stream().collect(Collectors.toMap(MerchantList::getId, Function.identity()));
        return list.stream().map(x -> {
            ProductListDTO dto = new ProductListDTO();
            dto.setProductId(x.getId());
            dto.setProductName(x.getProductName());
            dto.setMerchantId(x.getMerchantId());
            MerchantList result = merchantListResultMap.get(x.getMerchantId());
            Optional.ofNullable(result).ifPresent(m -> dto.setMerchantName(result.getMerchantName()));
            return dto;
        }).toList();
    }

    @Override
    public List<ProductChargedListDTO> listProductCharged(Long productId) {
        List<ProductUnitPriceConfigVersionDO> list = productUnitPriceConfigVersionExtendMapper.listProductUnitPriceConfigVersionByProductId(productId);
        return list.stream().map(productAssembler::toProductChargedListDTO).toList();
    }

    @Override
    public List<ProductChargedInfoDTO> listProductCharged(Set<Long> productIds) {
        List<ProductChargedInfoResult> list = productUnitPriceConfigExtendMapper.listByProductIds(productIds);
        return list.stream().map(productAssembler::toProductChargedInfoDTO).toList();
    }

    @Override
    public ProductAdvanceChargeRecordDetailDTO getProductAdvanceChargeRecordDetail(Long id) {
        ProductAdvanceChargeRecordDO productAdvanceChargeRecordDO = productAdvanceChargeRecordExtendMapper.selectByPrimaryKey(id);
        return productAssembler.toProductAdvanceChargeRecordDetailDTO(productAdvanceChargeRecordDO);
    }

    @Override
    public PageResult<ProductAdvanceChargeRecordPageDTO> productAdvanceChargeRecordPageQuery(ProductAdvanceChargeRecordPageCommand command) {
        Long merchantId = command.getMerchantId();
        Long productId = command.getProductId();
        Date startDate = DateFormatUtils.X.getDateByStr(command.getStartAdvanceChargeDate());
        Date endDate = DateFormatUtils.X.getDateByStr(command.getEndAdvanceChargeDate());
        long total = productAdvanceChargeRecordExtendMapper.countProductAdvanceChargeRecordPageQuery(merchantId, productId, startDate, endDate);
        if (total == 0) {
            return PageResult.success(Collections.emptyList(), total);
        }
        int offset = PageQueryUtils.X.getOffset(command.getPageNum(), command.getPageSize());
        List<ProductAdvanceChargeRecordDO> results = productAdvanceChargeRecordExtendMapper.productAdvanceChargeRecordPageQuery(merchantId, productId, startDate, endDate, offset, command.getPageSize());
       Set<Long> productIds = results.stream().map(ProductAdvanceChargeRecordDO::getProductId).collect(Collectors.toSet());
        Map<Long, ProductInfoDO> productMap = productInfoExtendMapper.listProductByIds(productIds).stream().collect(Collectors.toMap(ProductInfoDO::getId, Function.identity()));
        List<ProductAdvanceChargeRecordPageDTO> dtoList = results.stream().map(x -> {
            ProductAdvanceChargeRecordPageDTO productAdvanceChargeRecordPageDTO = productAssembler.toProductAdvanceChargeRecordPageDTO(x);
            ProductInfoDO productInfoDO = productMap.get(x.getProductId());
            Optional.ofNullable(productInfoDO).ifPresent(y -> productAdvanceChargeRecordPageDTO.setProductName(y.getProductName()));
            return productAdvanceChargeRecordPageDTO;
        }).toList();
        return PageResult.success(dtoList, total);
    }

    @Override
    public List<ProductInfoDO> getProductListByMerchantId(Long merchantId) {
        List<ProductInfoDO> productInfoDOS = productInfoExtendMapper.listProductByMerchantId(merchantId);
        return productInfoDOS;
    }

    @Override
    public ProductAdvanceChargeRecordStatisticsDTO productAdvanceChargeRecordStatistics(ProductAdvanceChargeRecordPageCommand command) {
        Long merchantId = command.getMerchantId();
        Long productId = command.getProductId();
        Date startDate = DateFormatUtils.X.getDateByStr(command.getStartAdvanceChargeDate());
        Date endDate = DateFormatUtils.X.getDateByStr(command.getEndAdvanceChargeDate());
        ProductAdvanceChargeRecordStatisticsResult result = productAdvanceChargeRecordExtendMapper.productAdvanceChargeRecordStatistics(merchantId, productId, startDate, endDate);
        if (Objects.isNull(result)) {
            return null;
        }
        ProductAdvanceChargeRecordStatisticsDTO dto = new ProductAdvanceChargeRecordStatisticsDTO();
        dto.setTotalConsumedAmount(result.getTotalConsumedAmount());
        dto.setTotalResidueAdvanceChargeAmount(result.getTotalAdvanceChargeAmount().subtract(result.getTotalConsumedAmount()));
        return dto;
    }

    @Override
    public PageResult<AdvanceChargePageDTO> advanceChargePageQuery(AdvanceChargePageCommand command) {
        Long merchantId = command.getMerchantId();
        long total = merchantExternalService.countMerchantPageQuery(merchantId);
        if (total == 0) {
            return PageResult.success(Collections.emptyList(), total);
        }
        int offset = PageQueryUtils.X.getOffset(command.getPageNum(), command.getPageSize());
        int pageSize = command.getPageSize();
        List<MerchantPageDTO> results = merchantExternalService.merchantPageQuery(command, offset, pageSize);
        if(results.isEmpty()){
            return PageResult.success(Collections.emptyList(), 0);
        }
        Set<Long> merchantIds = results.stream().map(MerchantPageDTO::getId).collect(Collectors.toSet());
        Map<Long, AdvanceChargeResult> advanceChargeMap = productAdvanceChargeRecordExtendMapper.listAdvanceChargeResultByMerchantIds(merchantIds).stream().collect(Collectors.toMap(AdvanceChargeResult::getMerchantId, Function.identity()));
        StrBuilder strBuilder = new StrBuilder();
        List<AdvanceChargePageDTO> dtoList = results.stream().map(x -> {
            AdvanceChargePageDTO advanceChargePageDTO = new AdvanceChargePageDTO();
            // 获取商户下所有的产品
            List<ProductInfoDO> productInfoDOS =productInfoExtendMapper.listProductByMerchantId(x.getId());
            if (CollUtil.isNotEmpty(productInfoDOS)) {
                List<Long> productIds = new ArrayList<>();

                for (ProductInfoDO productInfoDO : productInfoDOS) {
                    productIds.add(productInfoDO.getId());
                    strBuilder.append(productInfoDO.getProductName());
                    strBuilder.append(";");
                }
                strBuilder.del(strBuilder.length() - 1, strBuilder.length());
                advanceChargePageDTO.setProductIds(productIds);
                advanceChargePageDTO.setProductName(strBuilder.toString());
                strBuilder.reset();
            }
            advanceChargePageDTO.setMerchantId(x.getId());
            advanceChargePageDTO.setSettlementNumber(0);
            advanceChargePageDTO.setSettlementAmount(BigDecimal.ZERO);
            advanceChargePageDTO.setAdvanceChargeAmount(BigDecimal.ZERO);
            advanceChargePageDTO.setConsumedAmount(BigDecimal.ZERO);
            advanceChargePageDTO.setResidueAdvanceChargeAmount(BigDecimal.ZERO);
            advanceChargePageDTO.setMerchantName(x.getMerchantName());
            advanceChargePageDTO.setCreateTime(x.getCreateTime());
            AdvanceChargeResult advanceChargeResult = advanceChargeMap.get(x.getId());
            Optional.ofNullable(advanceChargeResult).ifPresent(ca -> {
                advanceChargePageDTO.setSettlementNumber(ca.getSettlementNumber());
                advanceChargePageDTO.setSettlementAmount(ca.getSettlementAmount());
                advanceChargePageDTO.setAdvanceChargeAmount(ca.getAdvanceChargeAmount());
                advanceChargePageDTO.setConsumedAmount(advanceChargePageDTO.getSettlementAmount());
                advanceChargePageDTO.setResidueAdvanceChargeAmount(advanceChargePageDTO.getAdvanceChargeAmount().subtract(advanceChargePageDTO.getConsumedAmount()));
            });
            return advanceChargePageDTO;
        }).toList();
        return PageResult.success(dtoList, total);
    }

    @Override
    public List<ProductPageDTO> productPageQueryList(ProductPageCommand command) {
        return null;
    }

    @Override
    public List<ProductDetailDTO> listAllProductByMerchantId(Long merchantId, Integer productType) {
        List<ProductDetailResult> results = productInfoExtendMapper.listAllProductByMerchantId(merchantId, productType);
        return results.stream().map(productAssembler::toProductDetailDTO).toList();
    }

    @Override
    public List<ProductListDTO> listProductNAbutment() {
        List<ProductInfoDO> list = productInfoExtendMapper.listProductNAbutment();
        if (CollUtil.isEmpty(list)) {
            return CollUtil.newArrayList();
        }
        Set<Long> merchantIds = list.stream().map(ProductInfoDO::getMerchantId).collect(Collectors.toSet());
        Map<Long, MerchantList> merchantListResultMap = merchantExternalService.listMerchantByIds(merchantIds).stream().collect(Collectors.toMap(MerchantList::getId, Function.identity()));
        return list.stream().map(x -> {
            ProductListDTO dto = new ProductListDTO();
            dto.setProductId(x.getId());
            dto.setProductName(x.getProductName());
            dto.setMerchantId(x.getMerchantId());
            MerchantList result = merchantListResultMap.get(x.getMerchantId());
            Optional.ofNullable(result).ifPresent(m -> dto.setMerchantName(result.getMerchantName()));
            return dto;
        }).toList();
    }

    @Override
    public List<ProductListDTO> listProductByMerchantIdAndUserId(Long merchantId, Long userId) {

        List<ProductInfoDO> list = productInfoExtendMapper.listProductByMerchantIdAndUserId(merchantId).stream().filter(x -> !x.getProductName().contains("暂停")).toList();

        if (CollUtil.isEmpty(list)) {
            return CollUtil.newArrayList();
        }
        Set<Long> merchantIds = list.stream().map(ProductInfoDO::getMerchantId).collect(Collectors.toSet());
        Map<Long, MerchantList> merchantListResultMap = merchantExternalService.listMerchantByIds(merchantIds).stream().collect(Collectors.toMap(MerchantList::getId, Function.identity()));
        return list.stream().map(x -> {
            ProductListDTO dto = new ProductListDTO();
            dto.setProductId(x.getId());
            dto.setProductName(x.getProductName());
            dto.setMerchantId(x.getMerchantId());
            MerchantList result = merchantListResultMap.get(x.getMerchantId());
            Optional.ofNullable(result).ifPresent(m -> dto.setMerchantName(result.getMerchantName()));
            return dto;
        }).toList();

    }

    @Override
    public List<ProductListDTO> allProducts() {

        List<ProductInfoDO> list = productInfoExtendMapper.allProducts();
        if (CollUtil.isEmpty(list)) {
            return CollUtil.newArrayList();
        }
        Set<Long> merchantIds = list.stream().map(ProductInfoDO::getMerchantId).collect(Collectors.toSet());
        Map<Long, MerchantList> merchantListResultMap = merchantExternalService.listMerchantByIds(merchantIds).stream().collect(Collectors.toMap(MerchantList::getId, Function.identity()));
        return list.stream().map(x -> {
            ProductListDTO dto = new ProductListDTO();
            dto.setProductId(x.getId());
            dto.setProductName(x.getProductName());
            dto.setMerchantId(x.getMerchantId());
            dto.setStatus(x.getProductStatus());
            MerchantList result = merchantListResultMap.get(x.getMerchantId());
            Optional.ofNullable(result).ifPresent(m -> dto.setMerchantName(result.getMerchantName()));
            return dto;
        }).toList();
    }

    /**
     * 查询产品禁用列表
     *
     * @param productBanDetails
     * @return
     */
    @Override
    public List<ProductListDTO> getAListOfBannedProducts(ProductBanDetails productBanDetails) {
        //根据产品id查询产品禁用列表
        List<ProductInfoDO> list = productInfoExtendMapper.getAListOfBannedProducts(productBanDetails);
        if (CollUtil.isEmpty(list)) {
            return CollUtil.newArrayList();
        }
        Set<Long> merchantIds = list.stream().map(ProductInfoDO::getMerchantId).collect(Collectors.toSet());
        Map<Long, MerchantList> merchantListResultMap = merchantExternalService.listMerchantByIds(merchantIds).stream().collect(Collectors.toMap(MerchantList::getId, Function.identity()));
        return list.stream().map(x -> {
            ProductListDTO dto = new ProductListDTO();
            dto.setProductId(x.getId());
            dto.setProductName(x.getProductName());
            dto.setMerchantId(x.getMerchantId());
            dto.setStatus(x.getProductStatus());
            MerchantList result = merchantListResultMap.get(x.getMerchantId());
            Optional.ofNullable(result).ifPresent(m -> dto.setMerchantName(result.getMerchantName()));
            return dto;
        }).toList();
    }
}
